// create 命令的所有逻辑

const axios = require("axios");
// const ora = require("ora");
const inquirer = require("inquirer");
const ncp = require("ncp").ncp;
const path = require("path");
const fs = require("fs");
const MetalSmith = require("metalsmith");
const { render } = require("consolidate").ejs;
const { promisify } = require("util");
let downloadGitRepo = require("download-git-repo");
// downloadGitRepo = promisify(downloadGitRepo); // downloadGitRepo 异步函数转化成 promise 对象
const { downloadDirectory } = require("./constants"); // 本地临时目录
const token_access = "69fd0404d795eb8ecd85159a9e166df3";

// ncp 转成 promise
const ncpPromise = promisify(ncp);
// render 转成 promise
const renderPromise = promisify(render);

// 获取组织下的所有仓库 （axios）
const fectchReposList = async () => {
  const repoUrl = `https://gitee.com/api/v5/orgs/why-cli/repos?access_token=${token_access}&type=all&page=1&per_page=20`;
  const { data } = await axios(repoUrl);
  return data;
};

// 获取指定仓库的tag
const fetchTagsList = async (repo) => {
  const url = `https://gitee.com/api/v5/repos/why-cli/${repo}/tags?access_token=69fd0404d795eb8ecd85159a9e166df3`;
  const { data } = await axios(url);
  return data;
};

// 封装 loading 函数,利用柯里化的思想
const waitFnLoading =
  (fn, message) =>
  async (...args) => {
    // 显示loading
    const ora = await (await import("ora")).default;
    const spinner = ora(message);
    spinner.start();
    // 拉取组织下的所有仓库
    let result = await fn(...args);
    // 隐藏loading
    spinner.succeed();
    return result;
  };

// 包装 downloadGitRepo 函数，返回promise
const downloadGitRepoPromise = (url, dest) => {
  return new Promise((resolve, reject) => {
    downloadGitRepo(url, dest, { clone: true }, function (err) {
      console.log(err ? err : "Success");
      if (err) {
        reject(err);
      } else {
        resolve();
      }
    });
  });
};

// 下载模板
const download = async (repo, tag) => {
  var api = `direct:https://gitee.com/why-cli/${repo}.git`;
  if (tag) {
    api += `#${tag}`;
  }
  const dest = `${downloadDirectory}/${repo}`;
  // await downloadGitRepo(api, downloadDirectory);
  await downloadGitRepoPromise(api, dest);
  return dest;
};

// https://docs.github.com/en/rest/reference/repos
// https://gitee.com/api/v5/swagger#/getV5OrgsOrgRepos
module.exports = async (projectName) => {
  // console.log(`create 命令的逻辑代码 ： ${projectName}`);
  // 拉取组织下的所有仓库
  let repos = await waitFnLoading(fectchReposList, "fetching template...")();
  // 获取所有仓库的名称
  repos = repos.map((repo) => repo.name);
  // 选择仓库，也就是选择模板, 返回选择后的模板信息
  // 返回值是一个 promise， 使用 await 接收一下返回值
  const { repo } = await inquirer.prompt({
    name: "repo", // 选择后结果的key
    type: "list", // list  input checkox ...
    message: "please choise a template to create a project", // 提示信息
    choices: repos, // 所有选项
  });
  console.log(repo);

  // 获取模板的 tags
  let tags = await waitFnLoading(fetchTagsList, "fetching tags...")(repo);
  tags = tags.map((item) => item.name);
  console.log(tags);
  // 选择 tags
  let { tag } = await inquirer.prompt({
    name: "tag",
    type: "list",
    message: "please choise tag to create a project",
    choices: tags,
  });
  console.log(tag);

  // 下载模板到本地临时目录
  const tempDest = await waitFnLoading(download, "download template ...")(
    repo,
    tag
  );
  console.log(tempDest); // 本地临时目录，存放了下载的模板

  // 拷贝文件 ncp
  // path.resolve()   // 当前目录 D:\Project\SourceCode\mine-projects\why-cli
  // console.log(projectName); // [ 'my-project' ]
  // console.log(path.resolve(projectName[0])); // D:\Project\SourceCode\mine-projects\why-cli\my-project
  // 将临时目录中的文件，全部拷贝到，当前命令执行目录中，文件夹名就是项目名称

  if (!fs.existsSync(path.join(tempDest, "ask.js"))) {
    // 简单模板，直接拷贝
    console.log("简单模板");
    await ncpPromise(tempDest, path.resolve(projectName[0]));
  } else {
    // 如果存在ask.js文件，则是复杂模板
    // 复杂模板，先编译，再拷贝
    console.log("复杂模板");
    // 让用户填写信息
    // metalSmith就是用来编译模板的,只要是模板编译都需要
    await new Promise((resolve, reject) => {
      MetalSmith(__dirname) // 如果传入路径，就会遍历当前路径src文件夹
        .source(tempDest)
        .destination(path.resolve(projectName[0]))
        .use(async (files, metal, done) => {
          const ask = require(path.join(tempDest, "ask.js"));
          const answer = await inquirer.prompt(ask);
          const meta = metal.metadata();
          Object.assign(meta, answer);
          // console.log(answer);
          delete files["ask.js"];
          done();
        })
        .use(async (files, metal, done) => {
          // 编译模板
          const answer = metal.metadata();
          Reflect.ownKeys(files).forEach(async (file) => {
            console.log(file);
            if (
              file.includes(".js") ||
              (file.includes(".json") && !file.includes(".ejs"))
            ) {
              let content = files[file].contents.toString();
              if (content.includes("<%")) {
                content = await renderPromise(content, answer);
                files[file].contents = Buffer.from(content);
              }
            }
          });
          done();
        })
        .build((err) => {
          if (err) {
            reject(err);
          } else {
            resolve();
          }
        });
    });

    //  拿到用户填写的信息，编译模板
  }
};
